﻿namespace BattleCity.GameLogic

open System
open FMath
open UI
open Types
open Casanova
open Coroutines
open GameState
open TanksCollisionsFunctions
open BulletsCollisionsFunctions

open Microsoft.Xna.Framework
open Microsoft.Xna.Framework.Input

module GameStateFlow =

 let random = System.Random()

 let playerFireTrigger = ref false;
 let pressedPad = ref Keys.None

 let enemyFireTriggers = [| false; false; false |]
 let mutable private newEnemyDirection = Vector2.Down
 let mutable private newEnemyDirectionIndex = 0

 let rec Update (deltaT : float32) =
  let dt : float<s> = (deltaT |> float) * 1.0<s>
  
  UpdateBlocks ()
  UpdateBullets dt
  let catchedBonus = UpdateBonus dt
  let updatedEnemies = UpdateEnemies dt
  if(GameState.gameState.PlayerLives.Value > 0) then
   UpdatePlayer (dt, catchedBonus, updatedEnemies)
  

 and UpdatePlayer (dt : float<s>, catchedBonus : ref<Bonus>, updatedTanks : ref<List<int * Tank>>) = 
  
  GameState.playerShoot.Value <- false
  GameState.playerSpawn.Value <- false

  match gameState.PlayerState.Value with
   | PlayerState.Spawning ->  gameState.PlayerTransitionClock.Value <- gameState.PlayerTransitionClock.Value + dt
                              GameState.gameState.PlayerTank := !GameState.gameState.PlayerTank
                              if gameState.PlayerTransitionClock.Value >= 1.0<s> then
                               gameState.PlayerState.Value <- PlayerState.Playing
                               gameState.PlayerTransitionClock.Value <- 3.0<s>
                                

   | PlayerState.Destroyed -> gameState.PlayerTransitionClock.Value <- gameState.PlayerTransitionClock.Value - dt
                              if gameState.PlayerTransitionClock.Value <= 0.0<s> then
                               GameState.gameState.PlayerLives.Value <- GameState.gameState.PlayerLives.Value - 1
                               if(GameState.gameState.PlayerLives.Value > 0) then
                                GameState.gameState.PlayerTank := GameState.playerTank
                                GameState.playerSpawn.Value <- true
                                gameState.PlayerTransitionClock.Value <- 0.0<s>
                                gameState.PlayerState.Value <- PlayerState.Spawning
                            
             
   | PlayerState.Playing ->
      let mutable KeyPressed = false;
      let mutable Direction = (!gameState.PlayerTank).Direction
      
      //Controllo se il giocatore ha preso un bonus per potenziare l'arma.
      let weapon = match catchedBonus.Value with 
                    | Bonus.WeaponUp -> match (!gameState.PlayerTank).Weapon with
                                         | WeaponType.SingleBullet -> WeaponType.DoubleBullet
                                         | WeaponType.DoubleBullet -> WeaponType.RenforcedBullet
                                         | WeaponType.RenforcedBullet -> WeaponType.RenforcedBullet
                    | _ -> (!gameState.PlayerTank).Weapon
       
      //Controllo se il giocatore ha preso un bonus per attivare lo scudo
      if catchedBonus.Value = Bonus.Shield then  gameState.PlayerTransitionClock.Value <- 10.0<s>
      if gameState.PlayerTransitionClock.Value > 1.0<s> then 
            GameState.shieldClock.Value <- GameState.shieldClock.Value + dt
            gameState.PlayerTransitionClock.Value <- gameState.PlayerTransitionClock.Value - dt
            if GameState.shieldClock.Value >= 0.1<s> then
                GameState.shieldEnabled.Value <- not GameState.shieldEnabled.Value
                GameState.shieldClock.Value <- 0.0<s>
      else  GameState.shieldEnabled.Value <- false
 

      let Position = (!gameState.PlayerTank).Position
      let mutable Velocity = 0.0<m/s>
      let keyBoardState = Keyboard.GetState()
      if keyBoardState.[Keys.Down] = KeyState.Down then
       KeyPressed <- true
       Direction <- Vector2<1>.Down
      if keyBoardState.[Keys.Up] = KeyState.Down then
       KeyPressed <- true
       Direction <- Vector2<1>.Up
      if keyBoardState.[Keys.Right] = KeyState.Down then
       KeyPressed <- true
       Direction <- Vector2<1>.Right
      if keyBoardState.[Keys.Left] = KeyState.Down then
       KeyPressed <- true
       Direction <- Vector2<1>.Left

      if KeyPressed = true then Velocity <- 12.0<m/s>

      if playerFireTrigger.Value then
        match weapon with
            | WeaponType.SingleBullet ->    let newBullet = CreateBulletState(Position, 30.0<m/s>, Direction, 1)
                                            gameState.BulletList := newBullet::(!gameState.BulletList)

            | WeaponType.DoubleBullet ->    let bulletOffset = Vector2<m>(0.7<m> * Direction.Y, 0.7<m> * Direction.X)
                                            let newFirstBullet = CreateBulletState((Position - bulletOffset), 30.0<m/s>, Direction, 1)
                                            let newSecondBullet = CreateBulletState((Position + bulletOffset), 30.0<m/s>, Direction, 1)
                                            gameState.BulletList := newFirstBullet::newSecondBullet::(!gameState.BulletList)

            | WeaponType.RenforcedBullet -> let newBullet = CreateBulletState(Position, 30.0<m/s>, Direction, 2)
                                            gameState.BulletList := newBullet::(!gameState.BulletList)

        playerFireTrigger.Value <- false
        GameState.playerShoot.Value <- true
  

      let newPosition = Position + Velocity * Direction * dt

      //verifica se il carro colliderà sicuramente con un altro carro nemico.
      let tankToTankCollision = if PlayerTankToETanksCollision(Square(newPosition, 2.8<m>), Direction) = true &&
                                   PlayerTankToETanksCollision((!gameState.PlayerTank).CollisionSquare, (!gameState.PlayerTank).Direction) = false then
                                    true
                                else
                                    false

      let tankToBlockCollision, isStealth =  match checkTankBlocksCollision(Square(newPosition, 2.8<m>), Direction) with
                                              | collision , stealth, turnBack ->  collision, stealth

      if tankOutofMap(newPosition) = false 
       && tankToBlockCollision = false
       && tankToTankCollision = false then

       let newPlayerState = CreateTankState(Direction, newPosition, Velocity, 
                                               newWheelRollTheta(newPosition,(!gameState.PlayerTank).Position, GameState.wheelRadius, (!gameState.PlayerTank).WheelRollTheta),
                                               PlayerTank, isStealth, None, weapon)

       //Verifico la collisione con i nemici che hanno aggiornato la loro posizione
       let mutable intersection = false
       for enemy in updatedTanks.Value do
        let enemyState = match enemy with (index, state) -> state
        intersection <- intersection || (newPlayerState.CollisionSquare.Intersect(enemyState.CollisionSquare, Side.Top) <> (0.0,0.0))
        intersection <- intersection || (newPlayerState.CollisionSquare.Intersect(enemyState.CollisionSquare, Side.Bottom) <> (0.0,0.0))
        intersection <- intersection || (newPlayerState.CollisionSquare.Intersect(enemyState.CollisionSquare, Side.Right) <> (0.0,0.0))
        intersection <- intersection || (newPlayerState.CollisionSquare.Intersect(enemyState.CollisionSquare, Side.Left) <> (0.0,0.0))

       if intersection then gameState.PlayerTank := CreateTankState(Direction, Position, Velocity, 0.0, PlayerTank, (!gameState.PlayerTank).IsStealth, None, weapon)
       else gameState.PlayerTank := newPlayerState

      else
       gameState.PlayerTank := CreateTankState(Direction, Position, Velocity, 0.0, PlayerTank, (!gameState.PlayerTank).IsStealth, None, weapon)



 (************************************ AGGIORNAMENTO DEI BLOCCHI *************************************)

 (* ad ogni ciclo aggiorno solo i blocchi che sono stati colpiti da proiettili *
  * e che sono contenuti nella ChangedBlockList.                                *)
 and UpdateBlocks () = 
  for block in gameState.ChangedBlockList.Value do
   match block with
    (i,j) -> let bricksArray = Types.getBricks(gameState.BlockGrid.[i,j].makeUp); 
             let mutable endLoop = 0 
             match gameState.BlockGrid.[i,j].makeUp with
              | SteelBrick(x) -> endLoop <- 1
              | CeramicBrick(x) -> endLoop <- 3
              | _ -> ()
             for i = 0 to endLoop do
              for j = 0 to endLoop do
               bricksArray =* ((!bricksArray).[i,j],i,j)

  gameState.ChangedBlockList.Value <- []



 (************************************ AGGIORNAMENTO DEI PROIETTILI *********************************)

 (* Aggiorna posizione dei proiettili nella BulletList e ne verifica la collisione.
  * Se i proiettili hanno colliso non li include nella lista dello stato successivo. *)
 and UpdateBullets(dt : float<s>) =
  
  GameState.playerDestroyed.Value <- false

  let changedBlockList = gameState.ChangedBlockList;
  let hittedEnemiesList = gameState.HittedEnemiesList;

  let hittedEnemyBullets = ref []
  
  gameState.BulletList := [
                           for bullet in !gameState.BulletList do
                            bullet.position := !bullet.position + !bullet.velocity * bullet.direction * dt
                            bullet.collisionSquare := Square(!bullet.position, bulletSize)
                            if blockHitted(bullet, changedBlockList) 
                            && checkPlBulletToTanksCollision(bullet, hittedEnemiesList) = false
                            && checkPBulletEbulletsCollision(!bullet.position, hittedEnemyBullets) = false then 
                             yield bullet 
                          ]
  
 
  gameState.EnemiesBulletList := [
                                   let counter = ref 0
                                   for bullet in !gameState.EnemiesBulletList do
                                       match bullet with 
                                        | (bulletState, tankIndex) -> 

                                            bulletState.position := !bulletState.position + !bulletState.velocity * bulletState.direction * dt
                                            bulletState.collisionSquare := Square(!bulletState.position, bulletSize)
                                            
                                            let playerHitted = checkEnBulletToPlTanksCollision(bulletState)
                                            if playerHitted then 
                                             if gameState.PlayerState.Value = PlayerState.Playing && gameState.PlayerTransitionClock.Value <= 1.0<s> then
                                              gameState.PlayerState.Value <- PlayerState.Destroyed
                                              gameState.PlayerTransitionClock.Value <- 1.0<s>
                                              GameState.playerDestroyed.Value <- true
                                              gameState.PlayerTank := !gameState.PlayerTank
                                            else 
                                             if bulletIsInHittedList(counter.Value, hittedEnemyBullets) = false
                                             && blockHitted(bulletState, changedBlockList) 
                                             && checkEnBulletToEnTanksCollision(bulletState, tankIndex) = false
                                             && checkEnBulletToEnBulletsCollision(!bulletState.position, tankIndex) = false then 
                                              yield bullet

                                       counter.Value <- counter.Value + 1
                                 ]

 (************************************ AGGIORNAMENTO DEI BONUS *********************************)
 and UpdateBonus (dt: float<s>) =

  GameState.bonusCatched.Value <- false
  
  let catchedBonus = ref Bonus.None
  gameState.BonusList := [
                            for bonus in !gameState.BonusList do
                             match bonus with 
                              | (bonusType, bonusPosition, bonusTime) ->
                                  if bonusTime > 0.0<s> then 
                                   if (bonusPosition - (!gameState.PlayerTank).Position).LenghtSquared < 2.0<m*m> && GameState.bonusCatched.Value = false then
                                     catchedBonus.Value <- bonusType
                                     GameState.bonusCatched.Value <- true
                                   else
                                    yield bonusType, bonusPosition, bonusTime - dt

                            for bonus in gameState.SpawnedBonus.Value do
                             match bonus with
                              | (bonusType, bonusPosition) ->
                                   yield bonusType, bonusPosition, 10.0<s>
                         ]

  gameState.SpawnedBonus.Value <- []
  
  //se ho preso il bonus nuclear distruggo tutti i nemici
  
  if catchedBonus.Value = Bonus.Nuclear then
    gameState.HittedEnemiesList.Value <- []
    let enemyCount = gameState.EnemiesList.Value.Length - 1
    for i = 0 to enemyCount do
     gameState.HittedEnemiesList.Value <- i::gameState.HittedEnemiesList.Value
  elif catchedBonus.Value = Bonus.Life then
    gameState.PlayerLives.Value <- gameState.PlayerLives.Value + 1

  catchedBonus


 (************************************ AGGIORNAMENTO CARRI NEMICI *********************************)

 and UpdateEnemies (dt : float<s>) =
 
  GameState.enemyDestroyed.Value <- false
 
  let hittedEnemiesList = gameState.HittedEnemiesList.Value
  let spawnedEnemiesList = ref []
  let updatedTanks = ref []

  gameState.EnemiesList := [ let indexCounter = ref 0
                             for tank in !gameState.EnemiesList do  
                                 let direction = ref (!tank).Direction
                                 let tankType = ref (!tank).TankType

                                 if newEnemyDirectionIndex = indexCounter.Value then
                                  direction.Value <- newEnemyDirection

                                 //se il carro è stato colpito verifico se è un carro bonus, faccio
                                 //lo spawn del relativo bonus e tolgo il carro dalla lista dei nemici.
                                 if isTankHitted(indexCounter) then
                                  if (!tank).Bonus <> None then 
                                   gameState.SpawnedBonus.Value <- ((!tank).Bonus,(!tank).Position)::gameState.SpawnedBonus.Value
                                 else
                                 //se il carro NON è stato colpito:

                                  //se il carro spara creo lo stato di un nuovo proiettile
                                  if enemyFireTriggers.[indexCounter.Value] then 
                                    let qualcosa = gameState.EnemiesBulletList 
                                    
                                    if (!tank).Weapon = WeaponType.DoubleBullet then
                                      let bulletOffset = Vector2<m>(0.7<m> * (!tank).Direction.Y, 0.7<m> * (!tank).Direction.X)
                                      let newFirstBullet = CreateBulletState(((!tank).Position - bulletOffset), 30.0<m/s>, (!tank).Direction, 1)
                                      let newSecondBullet = CreateBulletState(((!tank).Position + bulletOffset), 30.0<m/s>, (!tank).Direction, 1)
                                      gameState.EnemiesBulletList := (newFirstBullet,indexCounter.Value)::(newSecondBullet,indexCounter.Value)::(!gameState.EnemiesBulletList)
                                    else
                                      let newBulletState = CreateBulletState((!tank).Position, 30.0<m/s>, (!tank).Direction, 1)
                                      let newBullet = (newBulletState, indexCounter.Value)
                                      gameState.EnemiesBulletList := newBullet::(!gameState.EnemiesBulletList) 

                                    enemyFireTriggers.[indexCounter.Value] <- false

                                  //aggiorno la posizione del carro e verifico le collisioni
                                  let newPosition = (!tank).Position + (!tank).Velocity * direction.Value * dt
                                  let newCollisionSquare = Square(newPosition, 2.8<m>)

                                  
                                  //verifica se il carro colliderà con un altro carro.
                                  let tankToTankCollision = if ETankToTanksCollision(newCollisionSquare, direction.Value, indexCounter.Value) = true &&
                                                               ETankToTanksCollision((!tank).CollisionSquare, (!tank).Direction, indexCounter.Value) = false then
                                                                 true
                                                            else
                                                                 false
                                 
                                  let blockCollision, isStealth, turn =  match checkTankBlocksCollision(newCollisionSquare, direction.Value) with
                                                                              | collision , stealth, turn ->  if turn || tankToTankCollision then
                                                                                                                collision, stealth, true
                                                                                                              else
                                                                                                                collision, stealth, false
                                  if tankOutofMap(newPosition) = false 
                                   && blockCollision = false
                                   && tankToTankCollision = false then

                                   let newState = GameState.CreateTankState(direction.Value, 
                                                                     newPosition, 
                                                                     (!tank).Velocity, 
                                                                     newWheelRollTheta(newPosition, (!tank).Position, GameState.wheelRadius, (!tank).WheelRollTheta), 
                                                                     tankType.Value,
                                                                     isStealth,
                                                                     (!tank).Bonus, (!tank).Weapon)

                                   tank := newState
                                   updatedTanks.Value <- (indexCounter.Value, newState)::updatedTanks.Value

                                  else

                                   //se il carro sbatte contro un blocco che non puo distruggere è meglio che cambi direzione
                                   //lo includo nella lista dei carri che cambiano direzione e se ne occuperà l'AI
                                   if turn then
                                    let alreadyInList = ref false
                                    for tankIndex in GameState.turnList.Value do
                                        if indexCounter.Value = tankIndex then alreadyInList.Value <- true
                                    if not alreadyInList.Value then GameState.turnList.Value <- indexCounter.Value::turnList.Value

                                   tank := CreateTankState(direction.Value, (!tank).Position, (!tank).Velocity,
                                                          (!tank).WheelRollTheta, tankType.Value, (!tank).IsStealth, (!tank).Bonus, (!tank).Weapon)
                                   
                                   yield tank

                                 indexCounter.Value <- indexCounter.Value + 1
                             
                             //Controlla le collisioni sulle posizioni aggiornate dei carri nemici
                             for tank in updatedTanks.Value do
                              let index, state = match tank with (index, state) -> index, state
                              let intersection = ref false

                              for otherTank in updatedTanks.Value do
                               let otherIndex, otherState = match otherTank with (index, state) -> index, state

                               if index <> otherIndex then

                                    intersection.Value <- intersection.Value || (state.CollisionSquare.Intersect(otherState.CollisionSquare, Side.Top) <> (0.0,0.0))
                                    intersection.Value <- intersection.Value || (state.CollisionSquare.Intersect(otherState.CollisionSquare, Side.Bottom) <> (0.0,0.0))
                                    intersection.Value <- intersection.Value || (state.CollisionSquare.Intersect(otherState.CollisionSquare, Side.Right) <> (0.0,0.0))
                                    intersection.Value <- intersection.Value || (state.CollisionSquare.Intersect(otherState.CollisionSquare, Side.Left) <> (0.0,0.0))
                                 
                              indexCounter.Value <- 0

                              for tankStates in !gameState.EnemiesList do
                               if indexCounter.Value = index then
                                if intersection.Value then
                                 tankStates:= CreateTankState(state.Direction, (!tankStates).Position, (!tankStates).Velocity,
                                                        (!tankStates).WheelRollTheta, (!tankStates).TankType, (!tankStates).IsStealth, (!tankStates).Bonus, (!tankStates).Weapon)
                                yield tankStates
                               indexCounter.Value <- indexCounter.Value + 1

                                   
                             //se l'animazione di spawn dei nuovi nemici non è finita allora gli includo
                             //ancora nella SpawnedEnemiesList aumentandone il contatore
                             for spawnedTank in !gameState.SpawnedEnemiesList do
                               match spawnedTank with
                                (tankState, counter) -> if counter >= 1.0<s> then
                                                         yield Variable(fun () -> tankState)
                                                        else 
                                                         spawnedEnemiesList.Value <- (tankState, counter + dt * 4.0)::spawnedEnemiesList.Value
                           ] 


  if gameState.HittedEnemiesList.Value.Length > 0 then GameState.enemyDestroyed.Value <- true
  gameState.DestroyedEnemiesList := 
    [ 
        for destroyedTank in !gameState.DestroyedEnemiesList do
            match destroyedTank with
                (tankState, counter) -> if counter > 0.0<s> then
                                            //aggiorno il clock per l'animazione del nemicoo distrutto
                                            yield (tankState, counter - dt * 4.0)
                                        else 
                                            //aggiorno il contatore dei nemici distrutti
                                            gameState.DestroyedEnemies.Value <- gameState.DestroyedEnemies.Value + 1


        for index in gameState.HittedEnemiesList.Value do
            yield (!(!gameState.EnemiesList).[index] , 1.0<s>)
         
    ]
   
  //se un carro è stato colpito lo tolgo dalla lista dei carri che devono cambiare direzione   
  GameState.turnList.Value <- [  for turnIndex in GameState.turnList.Value do
                                    let hitted = ref false
                                    for index in gameState.HittedEnemiesList.Value do
                                        if turnIndex = index then hitted.Value <- true
                                    if not hitted.Value then yield turnIndex 
                              ]

  let qualcosa = GameState.turnList.Value

  //aggiorno il gameState con la lista dei nemici che sono in fase di generazione, 
  //ovvero quelli di cui l'animazione di spawn non è ancora terminata
  gameState.SpawnedEnemiesList :=  spawnedEnemiesList.Value
 
  gameState.HittedEnemiesList.Value <- []

  //ritorna la lista dei nemici con posizione aggiornata
  updatedTanks
 
 let mutable private input =
  let fire() =  co{
                        do! yield_
                        let fireButton = Keyboard.GetState().IsKeyDown(Keys.Space)
                        if fireButton && gameState.PlayerState.Value = PlayerState.Playing then
                            playerFireTrigger.Value <- true
                            do!  wait 0.7
                 }

  let joystick() = co{
                        do! yield_
                        if Keyboard.GetState().IsKeyDown(Keys.Up) then pressedPad.Value <- Keys.Up
                        if Keyboard.GetState().IsKeyDown(Keys.Down) then pressedPad.Value <- Keys.Down
                        if Keyboard.GetState().IsKeyDown(Keys.Left) then pressedPad.Value <- Keys.Left
                        if Keyboard.GetState().IsKeyDown(Keys.Right) then pressedPad.Value <- Keys.Right
                    }

  ( repeat_(fire()) .||> repeat_(joystick()) )
  
 let spawnPoint = ref 0
 let mutable private main = 

  //Genera un nuovo nemico solo se se nella mappa ne sono presenti meno di 3
  let spawnEnemy() =
   co{
        do! wait (random.Next(3,4) |> float)
        spawnPoint.Value <- random.Next(spawnPoint.Value, spawnPoint.Value + 4)
        if spawnPoint.Value > 4 then spawnPoint.Value <- spawnPoint.Value - 5
        if ((!gameState.EnemiesList).Length + (!gameState.DestroyedEnemiesList).Length) < 3 &&  gameState.GeneratedEnemies.Value < GameState.levelEnemyList.Value.Length then
         let direction = if spawnPoint.Value = 3 || spawnPoint.Value = 4 then 
                          Vector2<1>.Up
                         else 
                          Vector2<1>.Down
         let spawnedEnemy =  match GameState.levelEnemyList.Value.[gameState.GeneratedEnemies.Value] with

                             | Silver, bonus -> CreateTankState(direction, GameState.enemyStartSlotsPos.[spawnPoint.Value], 10.0<m/s>,
                                                             0.0, Enemy(Silver), false, bonus, WeaponType.SingleBullet )
                                             
                             | Black, bonus -> CreateTankState(direction, GameState.enemyStartSlotsPos.[spawnPoint.Value], 18.0<m/s>,
                                                             0.0, Enemy(Black), false, bonus, WeaponType.SingleBullet )

                             | Blue, bonus  ->  CreateTankState(direction, GameState.enemyStartSlotsPos.[spawnPoint.Value], 14.0<m/s>,
                                                             0.0, Enemy(Blue), false, bonus, WeaponType.DoubleBullet ) 

         gameState.SpawnedEnemiesList := (spawnedEnemy,0.0<s>)::!gameState.SpawnedEnemiesList
         gameState.GeneratedEnemies.Value <- gameState.GeneratedEnemies.Value + 1
         spawnPoint.Value <- spawnPoint.Value + 1
     }

   
  //Sceglie casualmente una nuova direzione per uno dei 3 nemici della mappa
 
  let changeEnemyDirection() = 

    let removeFromTurnList index =
     GameState.turnList.Value <- [ for tankIndex in GameState.turnList.Value do
                                     if index <> tankIndex then yield tankIndex  ]

    let isInTurnList index =  let isInList = ref false
                              for tankIndex in GameState.turnList.Value do
                                  if index = tankIndex then isInList.Value <- true
                              isInList.Value                           
    co{
        do! wait ((random.Next(5,9) |> float) * 0.1)

        newEnemyDirectionIndex <- newEnemyDirectionIndex + 1
        if newEnemyDirectionIndex > 3 then newEnemyDirectionIndex <- 0

        if newEnemyDirectionIndex < (!gameState.EnemiesList).Length then
            let tank = (!gameState.EnemiesList).[newEnemyDirectionIndex]
            let directionSelector = random.Next(1,90)

            let yDistanceFromPlayer = (!tank).Position.Y - (!gameState.PlayerTank).Position.Y
            let xDistanceFromPlayer = (!tank).Position.X - (!gameState.PlayerTank).Position.X

            if isInTurnList newEnemyDirectionIndex then

             if (!tank).Direction == Vector2<1>.Up || (!tank).Direction == Vector2<1>.Down then 
                if xDistanceFromPlayer > 0.0<m> then newEnemyDirection <- Vector2<1>.Left
                elif xDistanceFromPlayer <= 0.0<m> then newEnemyDirection <- Vector2<1>.Right

             if (!tank).Direction == Vector2<1>.Left || (!tank).Direction == Vector2<1>.Right then 
                if yDistanceFromPlayer > 0.0<m> then newEnemyDirection <- Vector2<1>.Up
                elif yDistanceFromPlayer <= 0.0<m> then newEnemyDirection <- Vector2<1>.Down   
          
             removeFromTurnList newEnemyDirectionIndex
            else
                if(directionSelector < 45) then
                 if xDistanceFromPlayer > 0.2<m> then newEnemyDirection <- Vector2<1>.Left
                 elif xDistanceFromPlayer < -0.2<m> then newEnemyDirection <- Vector2<1>.Right
                else
                 if yDistanceFromPlayer > 0.2<m> then newEnemyDirection <- Vector2<1>.Up
                 elif yDistanceFromPlayer < -0.2<m> then newEnemyDirection <- Vector2<1>.Down   
      }

  (repeat_(spawnEnemy()) .||> repeat_(changeEnemyDirection()))

 let mutable private fire = 

  //Genera un nuovo nemico solo se se nella mappa ne sono presenti meno di 3
  let fireOne() =
   co{
        do! yield_
        do!  wait ((random.Next(7,10) |> float) * 0.1)
        enemyFireTriggers.[0] <- true
     }

  let fireTwo() =
   co{
        do!  wait ((random.Next(7,10) |> float) * 0.1)
        do! yield_
        enemyFireTriggers.[1] <- true
     }

  let fireThree() =
   co{     
        do!  wait ((random.Next(7,10) |> float) * 0.1)
        enemyFireTriggers.[2] <- true
        do! yield_
     }

  ( repeat_(fireOne()) .||> repeat_(fireTwo()) .||> repeat_(fireThree()) )

 let update_movement() = main <- update_ai main 
 let update_fire() = fire <- update_ai fire
 let update_input() = input <- update_ai input